package cn.huaqingcheng.tianshu.core.sys.service;

import cn.huaqingcheng.tianshu.common.core.FileTool;
import cn.huaqingcheng.tianshu.common.core.StrTool;
import cn.huaqingcheng.tianshu.common.core.model.AttributeMap;
import cn.huaqingcheng.tianshu.common.core.model.TemporaryUrl;
import cn.huaqingcheng.tianshu.core.sys.SysFileProperties;
import cn.huaqingcheng.tianshu.core.sys.adapter.FileInfo;
import cn.huaqingcheng.tianshu.core.sys.adapter.FileStorageService;
import cn.huaqingcheng.tianshu.core.sys.model.SysFile;
import cn.huaqingcheng.tianshu.core.sys.model.SysFileScheme;
import cn.huaqingcheng.tianshu.core.sys.param.SysFileUploadParam;
import cn.huaqingcheng.tianshu.core.sys.param.SysFileUploadResponse;
import cn.huaqingcheng.tianshu.core.sys.repository.SysFileRepository;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import jakarta.inject.Provider;
import java.io.InputStream;
import java.net.URI;
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * SysFileServiceImpl
 */
@Service
@RequiredArgsConstructor
public class SysFileServiceImpl implements SysFileService {

    private final FileStorageServiceManager manager;

    private final SysFileRepository fileRepository;

    private final SysFileProperties fileProperties;

    private final Provider<SysFileService> thisProvider;

    @Override
    public SysFileUploadResponse upload(InputStream is, SysFileUploadParam param) {
        Long accountId = param.getAccountId();
        String originalName = param.getOriginalName();
        String path = FileTool.getUniquePath(StrTool.valueOf(accountId), originalName);

        SysFileScheme scheme = fileProperties.getScheme();
        AttributeMap<Object> attrs = AttributeMap.of();

        FileStorageService storageService = manager.select(scheme)
                .orElseThrow();
        FileInfo info = storageService.save(is, path, attrs);
        URI uri = info.uri();
        TemporaryUrl url = storageService.temporaryUrl(uri);

        SysFile file = new SysFile()
                .setUri(uri)
                .setScheme(scheme)
                .setPath(Objects.requireNonNull(uri.getPath()))
                .setQuery(uri.getQuery())
                .setMimeType(info.mimeType())
                .setByteSize(info.size())
                .setAccountId(accountId)
                .setGroupId(param.getGroupId())
                .setOriginalName(originalName)
                .setFileAttrs(attrs)
                .setStorageParams(info.params())
                .setIsPublic(Objects.isNull(accountId));
        thisProvider.get().save(file);

        return new SysFileUploadResponse()
                .setId(file.getId())
                .setUri(uri)
                .setUrl(url);
    }

    @Override
    public String toAbsoluteAddress(URI uri) {
        return getTemporaryUrl(uri).url();
    }

    @Override
    public ResponseEntity<?> read(URI uri, Boolean redirect) {
        TemporaryUrl tempUrl = getTemporaryUrl(uri);

        Duration age = Optional.ofNullable(tempUrl.expiration())
                .map(i -> {
                    Duration maxAge = Duration.between(Instant.now(), i);
                    Duration safeAge = maxAge.minusMinutes(1);
                    return safeAge.isPositive() ? safeAge : maxAge;
                })
                .orElseGet(() -> Duration.ofDays(7));

        CacheControl cacheControl = CacheControl.maxAge(age)
                .cachePublic()
                .immutable()
                .noTransform();
        if (redirect) {
            return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT)
                    .cacheControl(cacheControl)
                    .header("Location", tempUrl.url())
                    .headers(headers -> useTempHeader(headers, tempUrl))
                    .build();
        }

        return ResponseEntity.status(HttpStatus.OK)
                .cacheControl(cacheControl)
                .headers(headers -> useTempHeader(headers, tempUrl))
                .body(tempUrl);
    }

    private static void useTempHeader(HttpHeaders headers, TemporaryUrl tempUrl) {
        Map<String, String> map = tempUrl.headers();
        if (map != null) {
            map.forEach(headers::add);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(SysFile file) {
        SysFile saved = fileRepository.save(file);
        file.setId(saved.getId());
    }

    private @NotNull TemporaryUrl getTemporaryUrl(URI uri) {
        FileStorageService storageService = manager.select(uri.getScheme())
                .orElseThrow(() -> new IllegalArgumentException("不支持的存储类型: " + uri.getScheme()));
        return storageService.temporaryUrl(uri);
    }

}
